package com.monster.starter.security.request;

import com.monster.common.Constant;
import com.monster.exception.impl.MonsterNoTackException;
import com.monster.security.data.utils.AesUtils;
import com.monster.security.data.utils.RsaUtils;
import com.monster.starter.core.common.config.Config;
import com.monster.starter.core.common.config.http.HttpRequestConfig;
import com.monster.starter.core.common.entity.MonsterHttpServletRequest;
import com.monster.starter.core.http.request.Request;
import com.monster.starter.core.utils.IoUtils;
import com.monster.starter.core.utils.RequestMappingUtils;
import com.monster.starter.security.common.SecurityExceptionEnum;
import com.monster.starter.security.common.entity.SysRsaInfo;
import com.monster.starter.security.service.TransmissionCipherService;
import com.monster.utils.StringUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.method.HandlerMethod;

import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

/**
 * 处理跨越请求
 * @author kuang
 */
@Component
@Order(Constant.FILTER_SECURITY_ORDER)
public class MonsterRequestBodySignDecryptFilter extends OncePerRequestFilter {
    /**
     * 请求对象
     */
    @Autowired
    private HttpServletRequest request;
    /**
     * 请求的统一配置
     */
    @Autowired
    private HttpRequestConfig requestConfig;
    /**
     * 传输密钥管理
     */
    @Autowired
    private TransmissionCipherService cipherService;
    /**
     * 配置
     */
    @Autowired
    private Config config;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 请求里面的数据,数据为空直接返回空数据流
        String data = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        if (StringUtils.isNoneEmpty(data)) {
            // 获取注解
            HandlerMethod handlerMethod = RequestMappingUtils.getHandlerMethod(request);
            if (handlerMethod != null) {
                // 密钥信息
                SysRsaInfo rsa = null;
                // 获取方法
                Method method = handlerMethod.getMethod();
                // 获取方法上的注解
                Request res = method.getDeclaredAnnotation(Request.class);
                // 是否需要解密
                boolean isOk = (res != null && res.decrypt()) || !StringUtils.isEmpty(this.request.getHeader(requestConfig.getEncryptName()));
                if (isOk) {
                    // 需要解密
                    String key = this.request.getHeader(requestConfig.getEncryptName());
                    if (StringUtils.isEmpty(key)) {
                        throw new MonsterNoTackException(SecurityExceptionEnum.DECRYPT_ERROR,
                                StringUtils.format("从请求头里面没有获取到用于解密的密钥信息"));
                    }
                    rsa = getRsaInfo(rsa);
                    if (rsa == null) {
                        throw new MonsterNoTackException(SecurityExceptionEnum.DECRYPT_ERROR,
                                StringUtils.format("无法获取服务器的密钥信息：" + key));
                    }
                    // 解密key
                    key = RsaUtils.decryptByPrivate(rsa.getSPrivateCipher(), key);
                    if (key == null) {
                        throw new MonsterNoTackException(SecurityExceptionEnum.DECRYPT_ERROR,
                                StringUtils.format("无法解密前端传输的数据：" + key));
                    }
                    // 解密数据
                    data = AesUtils.decryptCBC(key.substring(0, 16), key.substring(16), data);
                    // 重新构造请求对象
                    String finalData = data;
                    request = new MonsterHttpServletRequest(request, ((MonsterHttpServletRequest)request).isRouteMode()){
                        @Override
                        public String getRequestURI() {
                            return super.getRequestURI();
                        }

                        @Override
                        public StringBuffer getRequestURL() {
                            return super.getRequestURL();
                        }

                        @Override
                        public String getServletPath() {
                            return super.getPath();
                        }

                        @Override
                        public String getSubdomainName() {
                            return super.getRoute();
                        }

                        /**
                         * 返回请求流中的字符串
                         * @return 请求流中的字符串
                         */
                        @Override
                        public String getBody () throws IOException {
                            return finalData;
                        }

                        @Override
                        public ServletInputStream getInputStream() throws IOException {
                            return IoUtils.createServletInputStream(finalData);
                        }
                    };
                }
                isOk = (res != null && res.sign()) || requestConfig.isSign();
                // 判断数据是否需要验证签名
                if (isOk) {
                    // 获取验签串
                    String sign = this.request.getHeader(requestConfig.getSignName());
                    if (StringUtils.isEmpty(sign)) {
                        throw new MonsterNoTackException(SecurityExceptionEnum.SIGN_ERROR, "无法获取到请求里面的签名串，请确认是否有签名");
                    }
                    // 验签
                    if (!StringUtils.isEmpty(data)) {
                        if (!RsaUtils.verifyByPublic(rsa.getCPublicCipher(), data, sign)) {
                            throw new MonsterNoTackException(SecurityExceptionEnum.SIGN_ERROR, "请求数据验证签名失败，请求被破环，请注意！");
                        }
                    }
                }
            }
        }
        filterChain.doFilter(request, response);
    }
    /**
     * 获取密钥信息
     * @param info 密钥实例
     * @return 密钥信息
     */
    private SysRsaInfo getRsaInfo (SysRsaInfo info) {
        if (info == null) {
            info = cipherService.get(request.getHeader(requestConfig.getCipherName()));
        }
        return info;
    }
}
